// Copyright (c) 2021 Presto Labs Pte. Ltd.
// Author: xguo

#include <string>
#include <memory>
#include <utility>

#include "coin2/exchange/base/order/trade_client.h"

#include "coin2/exchange/base/order/external.h"
#include "coin2/exchange/factory/order/system_components.h"

namespace coin2::exchange::base::order {

void TradeClient::AddConfig(const std::string& mea, const OrderConnectionConfig& config) {
  config_map_[mea] = config;
}

void TradeClient::SetCallback(Callback callback) { callback_ = callback; }

void TradeClient::SubmitOrderExecution(const OrderExecutionProto& order) {
  auto request = GetNativeClient(order.mea())->SubmitOrderExecution(order);
  request->handler = [this](std::shared_ptr<AsioHttpContext> context) {
    RawMessageProto raw_msg;
    raw_msg.set_type(OrderSubTopicId::REST_MESSAGE);
    *raw_msg.mutable_http_context() = context->ToProto();
    callback_(raw_msg);
  };
  driver_map_.at(order.mea())->PostAsyncContext(request);
}

void TradeClient::SubmitRestQuery(const RestQueryProto& query) {
  auto request = GetNativeClient(query.mea())->SubmitRestQuery(query);
  request->handler = [this](std::shared_ptr<AsioHttpContext> context) {
    RawMessageProto raw_msg;
    raw_msg.set_type(OrderSubTopicId::REST_MESSAGE);
    *raw_msg.mutable_http_context() = context->ToProto();
    callback_(raw_msg);
  };
  driver_map_.at(query.mea())->PostAsyncContext(request);
}

NativePrivateClient* TradeClient::GetNativeClient(const std::string& mea) {
  using coin2::exchange::factory::order::OrderSubsystemComponents;
  using coin2::exchange::order::OrderSystemConfig;
  using coin2::exchange::order::PrivateSubscriberConfig;

  if (client_map_.count(mea) == 0) {
    auto mea_obj = MarketExchangeApi::FromString(mea);

    if (!driver_map_.count(mea)) {
      driver_map_[mea] = std::make_unique<NativeHttpAsioDriver>(ioc_, mea_obj, config_map_.at(mea));
    }

    OrderSystemConfig sysconfig;
    PrivateSubscriberConfig psconfig;
    *sysconfig.mutable_connection_config() = config_map_.at(mea);

    OrderSubsystemComponents component;
    component.Create(mea_obj, sysconfig, psconfig, nullptr, driver_map_.at(mea).get());

    client_map_[mea] = std::move(component.rest_client());
  }
  return client_map_.at(mea).get();
}

}  // namespace coin2::exchange::base::order
